# -*- coding=utf-8 -*-
# Created Time: 2016年09月02日 星期五 11时55分57秒
# File Name: vr.py

"""
VR 相关接口 v2.0
"""

from utils.routes      import route
from utils             import mongo
from utils.lib_time    import dt_str_now

from loog.mylog         import flogger as logger
from configure.config  import DB_NAME, COLL_CLASSRES, COLL_CLASSPUSH,\
                              COLL_CHANNELPUB, COLL_CHANNELRES

import tornado, pymongo


@route(r'/vr/check', name='vr_check')
class VrCheckHandler(tornado.web.RequestHandler):
    """ vr端 检查是否有可更新包

    检测规则:
        正式环境:
            1. 机器上传 当前版本号&内部版本号
            2. 找到线上相应　当前版本号&内部版本号(去掉build号)的所有数据
            3. 返回第二步数据中 待升级最高版本号的数据
            4. 机器获取到正确数据后开始升级

        测试环境:
            1. 查找机器 sn 是否在测试推送中 (最近的一次推送)
            不在:
                走正式环境渠道
            在:
                对比 ver(low)
                ver 匹配:
                    返回升级信息
                ver 不匹配:
                    返回给vr端，无可用升级,信息
    """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def get(self):

        try:
            scope   = self.get_argument('scope', 'online') # test/online
            sn_id   = self.get_argument('sn_id', None)     # 设备序列号
            ver     = self.get_argument('ver', None)       # 机器版本号
            version = self.get_argument('internal_ver', None)   # 内部版本号
            language= self.get_argument('language', 'en')
            created = dt_str_now()
            data    = None

            if not sn_id or not ver or not version:
                raise ValueError('Parameter Error')

            if scope not in ['test', 'online']:
                raise ValueError('Parameter Error')

            # test log params
            params = {
                'scope'  :scope,
                'sn_id'  :sn_id,
                'ver'    :ver,
                'internal_ver':version,
                'created':created,
                'language':language
            }
            logger.info(params)
        except Exception, e:
            logger.exception(e)
            self.write({'code':1, 'info':'params error'})
            self.finish()
            return

        # 测试环境
        if scope == 'test':
            res = yield tornado.gen.Task(self.env_test, sn_id, ver)
            next_step, data = res[0], res[1]

            if next_step == 'online':
                #logger.info('No SNID Go to online')
                #scope = 'online'  # 到正式环境检查
                logger.info('404_NO_AVAL_PACKAGE:')
                logger.info(params)
                pass
            elif next_step == 'low':
                logger.info('Low didnt match')
                # 更新匹配失败结果到测试环境
                yield tornado.gen.Task(
                        self.upadte_sn_status,
                        sn_id,
                        data['_id'],
                        0,
                        '初始版本号不匹配')
                data = None
            else:
                pass

        # 正式环境
        if scope == 'online':
            logger.info('In Online')
            data = yield tornado.gen.Task(self.env_online, ver, version)

        bar = self.response(data, sn_id, scope, ver, language)
        self.write(bar)
        self.finish()

    @tornado.web.asynchronous
    @tornado.gen.engine
    def upadte_sn_status(self, sn, proj, status, remark, callback=None):
        """ 更新推送中对应项目sn状态 """

        query = {'sn_id':sn, 'project':proj}
        cn = '成功' if status else '失败'
        res_dict = {
            'status':status,
            'remark':remark,
            'cn'    :cn
        }
        yield tornado.gen.Task(
                            mongo.update,
                            DB_NAME,
                            COLL_CLASSRES,
                            query,
                            res_dict)
        callback(True)

    def response(self, p, sn_id, scope, ver, language):
        """ 构造返回数据 """

        # 返回字段，也是无更新时的返回
        results = {
                'code':0,
                'result':{
                     'sn_id':           sn_id,
                     'scope':           scope,
                     'ver_from':        ver,
                     'size':            0,
                     'availbe_update':  0,
                     'update_url':      '',
                     'md5':             '',
                     'ts':              '',
                     'info':            '',
                     'project':         '',
                     'create_time':     '',
                     'finger_point':    ''
                  }
            }

        if not p: return results

        # 语言可能 en, zh, ja
        try:
            info = p['content'][language]
        except Exception, ex:
            logger.exception(ex)
            if isinstance(p['content'], dict):
                info = p['content'].get('en', '')
            else:
                info = ''

        foo = {
            'ver'           :p['high'],
            'size'          :p['size'],
            'update_url'    :p['url'],
            'md5'           :p['md5'],
            'ts'            :p['ts'],
            'info'          :info,
            'project'       :p['_id'],
            'finger_point'  :p.get('finger', ''),
            'create_time'   :p.get('create_time',''),
            'availbe_update':1
        }
        results['result'].update(foo)

        return results


    @tornado.web.asynchronous
    @tornado.gen.engine
    def env_online(self, low, internal_ver, callback=None):
        """ 正式环境检查更新包规则

        UPDATE: 2016-11-17
            客户端上传的 内部版本号 需去掉build号再进行对比

            去掉 build的方法:
                因为 build 号总是 Bxxx
                所以去掉内部版本号最后5位字符，就行了。
        """

        # 处理内部版本为可对比的
        version = internal_ver[:-5]

        # 检查更新, 匹配结果中 max_str 最大的记录
        query = {'low':low, 'version':version}
        logger.info("Online check query: %s" % query)

        sort = [
            ['max_str', pymongo.DESCENDING],   # high 最大的包
            ['created', pymongo.DESCENDING]    # 最近的一个推送
        ]
        ps = yield tornado.gen.Task(
                                mongo.find_all,
                                DB_NAME,
                                COLL_CHANNELPUB,
                                query,
                                limit=1,
                                sort=sort)

        if not ps:   # 无更新
            callback(None)
            return

        callback(ps[0])
        return


    @tornado.web.asynchronous
    @tornado.gen.engine
    def env_test(self, sn, low, callback=None):
        """ 测试环境检查更新包规则 """

        # 第一步 找测试推送中是否有 sn号 对应的记录
        sn_query = {'dvs':{'$in':[sn]}}
        sort = [
            ['created', pymongo.DESCENDING]    # 最近的一个推送
        ]
        pushs = yield tornado.gen.Task(
                                mongo.find_all,
                                DB_NAME,
                                COLL_CLASSPUSH,
                                sn_query,
                                limit=1,
                                sort=sort)

        if not pushs:   # sn 在测试环境没有推送，返回，到正式渠道找包
            callback(('online', None))
            return

        push = pushs[0]
        # 第二步, 对比 push 和 机器的 low 版本号
        if push['low'] != low:
            callback(('low', push))   # 写原因
            return

        callback(('success', push))
        return

    def post(self, *argl, **argd):
        self.get(*argl, **argd)


@route(r'/vr/feedback', name='vr_feedback')
class VrFeedbackHandler(tornado.web.RequestHandler):
    """ 处理vr端升级状态 反馈结果

    Stuff:
        1. 根据 推送id 更新推送的 成功/失败 次数
        2. 记录推送结果到数据库, 有个测试结果页面需要显示这个数据
        3. 测试/正式 环境都使用的这个接口，通过参数 scope 来区分

        4. 测试环境　直接 update 更新统计值
        5. 正式环境　还要复杂点，每次更新时　需要检查是否有以前的记录
           如果有    需要将对应值减去一
           如果没有  大吉大利!
    """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def get(self):

        try:
            project = self.get_argument('project', None) # 推送/发布 id
            scope   = self.get_argument('scope', None)   # 域 test/online
            sn_id   = self.get_argument('sn_id', None)   # sn号
            ver     = self.get_argument('ver', None)     # 版本
            status  = int(self.get_argument('update_success',None))#1/0 成功/失败
            remark  = self.get_argument('remark', '')    # 备注
            created = dt_str_now()

            # test
            params = {
                'push_id' : project,
                'scope'   : scope,
                'sn_id'   : sn_id,
                'ver'     : ver,
                'status'  : status
            }
            logger.info(params)

            if not sn_id or not ver or not project:
                raise ValueError('Parameter Error')
            if status not in [0, 1]:
                raise ValueError('Parameter Error')
            if scope not in ['test', 'online']:
                raise ValueError('Parameter Error')
        except Exception, e:
            logger.exception(e)
            self.write({'code':1, 'info':'params error'})
            self.finish()
            return

        if not remark: remark = '升级成功' if status else '失败'
        cn = '成功' if status else '失败'

        if scope == 'test':

            query = {'project':project,'sn_id':sn_id}
            updates = {'cn':cn, 'remark':remark, 'status':status}

            try:
                # 需要将原记录查询出来
                one = yield tornado.gen.Task(
                                    mongo.find_one,
                                    DB_NAME,
                                    COLL_CLASSRES,
                                    query,
                                    {'status':1})
                if not one: raise ValueError('404_ONE')

                # 修改 sn_id 的状态
                yield tornado.gen.Task(
                                    mongo.update,
                                    DB_NAME,
                                    COLL_CLASSRES,
                                    query,
                                    updates)

                inc = {}
                if one['status'] == 1:
                    if status == 0:
                        inc = {'success':-1, 'fail':1}
                elif one['status'] == 0:
                    if status == 1:
                        inc = {'success':1, 'fail':-1}

                # 更新总量
                if inc:
                    yield tornado.gen.Task(
                                    self.update,
                                    COLL_CLASSPUSH,
                                    inc, project)
            except Exception, e:
                logger.exception(e)
                self.write({'code':2,'info':'package error'})
                self.finish()
                return

        if scope == 'online':

            # 第一次反馈的情况
            if status == 1:
                inc = {'success':1, 'fail':0}
            else:
                inc = {'success':0, 'fail':1}

            res_dict = {
                'project':project,
                'scope'  :scope,
                'sn_id'  :sn_id,
                'ver'    :ver,
                'status' :status,
                'remark' :remark,
                'created':created,
                'cn'     :cn,
                'avail'  :1        # 最新的记录是可用的
            }

            try:
                # 查出上一次的记录，如果有需要覆盖
                res_query = {'project':project, 'sn_id':sn_id, 'avail':1}
                result = yield tornado.gen.Task(
                                            mongo.find_one,
                                            DB_NAME,
                                            COLL_CHANNELRES,
                                            res_query)
                logger.info(result)
                if result:  # 极少数情况会执行这里面的逻辑
                    # update 渠道更新量, 覆盖上次的记录
                    if result['status'] == 1:
                        if status == 0:
                            inc.update({'success':-1})
                        else:
                            inc.update({'success':0})
                    else:
                        if status == 1:
                            inc.update({'fail':-1})
                        else:
                            inc.update({'fail':0})

                    # 将之前的记录置为　不可用
                    yield tornado.gen.Task(
                                        mongo.update,
                                        DB_NAME,
                                        COLL_CHANNELRES,
                                        {'_id':result['_id']},
                                        {'avail':0})

                logger.info(inc)
                yield tornado.gen.Task(self.update, COLL_CHANNELPUB, inc, project)

                # 存结果记录, 每次的记录都是要存到对应集合的
                yield tornado.gen.Task(
                                    mongo.insert,
                                    DB_NAME,
                                    COLL_CHANNELRES,
                                    res_dict)
            except Exception, e:
                logger.exception(e)
                self.write({'code':2,'info':'package error'})
                self.finish()
                return

        results = {
            'code':0,
            'result':{
               'sn_id':           sn_id,
               'ver':             ver,
               'update_success':  str(status),
               'scope':           scope
           }
        }

        self.write(results)
        self.finish()

    def post(self, *argl, **argd):
        self.get(*argl, **argd)

    @tornado.web.asynchronous
    @tornado.gen.engine
    def update(self, coll_p, inc, project, callback=None):
        """ 更新 测试结果量 """

        query   = {'_id':project}
        updates = {'$inc':inc}
        yield tornado.gen.Task(
                            mongo.inc,
                            DB_NAME,
                            coll_p,
                            query,
                            updates)
        callback(True)


